var App = {
   feeds: {},
   atomicService: null,
   messages: null,
   tabs: null,
   categories: [],
   services : null,
   servicesRDF: null,
   servicesTree: null,
   treeTemplate: null,
   statusMessage: null,
   xsltCache: new XSLTCache(),
   config: null,
   init: function() {

      this.messages = new MessageBundle(document.getElementById('app-messages'));

      var theClass = Components.classes["@milowski.com/atomic;1"];
      var theComponent = theClass.getService(Components.interfaces.nsISupports);
      this.atomicService = theComponent.wrappedJSObject;
      LOG.level = this.atomicService.config.logLevel;
      
      LOG.debug("Initializing App");

      this.tabs = document.getElementById('tabs');
      
      /*
       * Initial the RDF handling
       */
      this.statusMessage = document.getElementById('status');
      this.xsltCache.loadTransformFromURI('service2tree','chrome://atomic/content/service2tree-rdf.xsl',false,null);
      this.xsltCache.loadTransformFromURI('unloaded2tree','chrome://atomic/content/unloaded-service2tree-rdf.xsl',false,null);
      this.xsltCache.loadTransformFromURI('workspace','chrome://atomic/content/feed2workspace-rdf.xsl',false,null);
      this.xsltCache.loadTransformFromURI('collection-tree','chrome://atomic/content/collection2tree-rdf.xsl',false,null);
      this.xsltCache.loadTransformFromURI('feed-tree','chrome://atomic/content/feed2tree-rdf.xsl',false,null);
      this.xsltCache.loadTransformFromURI('local-workspace','chrome://atomic/content/localws2workspace-rdf.xsl',false,null);
      var currentApp = this;
      
      LOG.debug("Loading default entry...");
      /*
       * load the entry template
       */
      HTTP("GET","chrome://atomic/content/default-entry.xml",{
         synchronizedRequest: true,
         onSuccess: function(status,document) {
            var serializer = new XMLSerializer();
            currentApp.defaultEntry = serializer.serializeToString(document);
         },
         onFailure: function(status,document) {
            alert("Cannot load chrome://atomic/content/default-entry.xml");
         }
      });
      
      
      try {
      /*
       * if there isn't a config url, run setup
       */
      if (!this.atomicService.config || !this.atomicService.config.configURL) {
         level = Logger.INFO;
         var data = {};
         window.openDialog("chrome://atomic/content/setup.xul","setup","chrome,modal",data);
         if (data.ok) {
            this.atomicService.config.configURL = data.configURL;
            this.atomicService.config.autoTitle = data.autoTitle;
            this.atomicService.config.defaultType = data.defaultType;
            this.atomicService.config.updatePreferences();
         }      
      }
      
      

      /*
       * if the URL is a file and doesn't exist, create it.
       */
      if (this.atomicService.config.configURL.indexOf("file:")==0) {
         if (!this.doesFileURLExist(this.atomicService.config.configURL)) {
            LOG.debug("Configuration file "+this.atomicService.config.configURL+" does not exist.  Creating blank instance.");
            // create an empty configuration of services
            var wdoc = document.implementation.createDocument(ATOMIC_CONFIG_NS,"configuration",null);
            wdoc.documentElement.appendChild(wdoc.createElementNS(ATOMIC_CONFIG_NS,"services"));
            this.storeDocument(this.atomicService.config.configURL,wdoc);
         }
      }
      } catch (e) {
         throw "Cannot configure atomic due to: "+e;
      }
      
      var currentApp = this;
      
      LOG.debug("Initializing services tree...");
      // TODO: make service an JS object (e.g. new Service())
      this.services = Service;
      this.services.init({
         title: null,
         timeout: _TIMEOUT_,
         service: null,
         xsltCache: this.xsltCache,
         logLevel: LOG.level,
         plugins: this.atomicService.plugins,
         collectionHandler: {
            init: function(doc,parent,xsltCache) {},
            showCollection: function(collection) { currentApp.setStatusMessage(""); currentApp.showCollection(collection); },
            showLoadingCollection: function(collection) { currentApp.setStatusMessage("Loading "+collection.url); }
         }
      });

      this.services.serviceShowHandler = function(service) {
         currentApp.showService(service);
      };

      LOG.debug("Attemping to load configuration file "+this.atomicService.config.configURL);
      /*
       * load the configuraiton and services
       */
      if (this.atomicService.config.document) {
         LOG.debug("Loading services...");
         this.loadServices();
      } else {
         LOG.debug("Loading configuration...");
         this.atomicService.config.load({
            onSuccess: function(theConfig) {
               LOG.debug("Configuration "+currentApp.atomicService.config.configURL+" loaded.");
               LOG.logDocument(LOG.DEBUG,currentApp.atomicService.config.document);
               currentApp.loadServices();
            },
            onFailure: function(status,document,ex) {
               alert("Cannot load "+currentApp.atomicService.config.configURL+", status="+status+", exception="+ex);
            }
         });
      }
      
      /*
       * load the plugins
       */
      LOG.debug("Loading plugins...");
      if (!this.atomicService.pluginsLoaded) {
         this.atomicService.loadPlugins();
      }
      
   },
   loadServices: function() {
      if (!this.atomicService.loaded) {
         LOG.debug("Loading services...");
         this.atomicService.loadServices();
      }
      LOG.debug("Initializing display...");
      this._initServicesDisplay();
      this._attemptServiceLoad();
   },
   _attemptServiceLoad: function() {
      LOG.debug("Retrying services that haven't been loaded.");
      var currentApp = this;
      for (var url in this.atomicService.services) {
         var service = this.atomicService.services[url];
         if (!service.loaded) {
            var doLoad = function(target) {
               try {
                  target.introspect({
                     timeout: _TIMEOUT_,
                     onSuccess: function() {
                        LOG.debug(target.url+" is loaded!");
                        currentApp._updateServiceDisplay(target);
                     },
                     onFailure: function(status,doc,text) {
                        LOG.debug(target.url+" is not available!");
                        if (status<0) {
                           // Ignore exception but log it
                           LOG.severe("Failed to handle service "+target.url+": "+text);
                        } else if (status!=0) {
                           alert("Failed to contact service "+target.url+" ("+status+") due to:\n"+(doc ? doc.documentElement.textContent.substring(0,200) : ""));
                        } 
                        currentApp._updateServiceDisplay(target);
                     }
                  });
               } catch (e) {
                  alert("Introspection failed for "+target.url+": "+e);
               }
            }
            doLoad(service);
         }
         if (service.google) {
            var doLogin = function(target,app) {
               app.atomicService.googleClientLogin(target.username,target.password,{
                  timeout: _TIMEOUT_,
                  onSuccess: function(auth) {
                     target.authorization = "GoogleLogin auth="+auth;
                     LOG.info(target.authorization);
                  },
                  onFailure: function(status,doc,text) {
                     alert("("+status+") Failed to login as "+target.username+" for blog:\n"+target.url+"\n"+text);
                  }
               });
            };
            doLogin(service,this);
         }
      }
   },
   _initServicesDisplay: function() {
      this.services.services = this.atomicService.services;
      this.services._updateServicesDisplay();
   },
   _initServiceDisplay: function(service) {
      this._updateServiceDisplay(service,false);
   },
   _updateServiceDisplay: function(service,refreshOnly) {
       // TODO: this should be a public method
      this.services._updateServiceDisplay(service);
   },
   getCharPref: function(name) {
      try {
         return this.atomicService.config.prefBranch.getCharPref(name);
      } catch (ex) {
         return null;
      }
   },
   getBoolPref: function(name) {
      try {
         return this.atomicService.config.prefBranch.getBoolPref(name);
      } catch (ex) {
         return false;
      }
   },
   getIntPref: function(name) {
      try {
         return this.atomicService.config.prefBranch.getIntPref(name);
      } catch (ex) {
         return -1;
      }
   },
   setStatusMessage: function(message){
      DOMUtil.clearChildren(this.statusMessage);
      if (message) {
         this.statusMessage.appendChild(this.statusMessage.ownerDocument.createTextNode(message));
      }
   },
   storeDocument: function(urlSpec,document) {
      if (urlSpec.indexOf("file:")==0) {
         var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
         var url = ioService.newURI(urlSpec,null,null);
         url = url.QueryInterface(Components.interfaces.nsIFileURL);
         var os = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
         os.init(url.file,0x02 | 0x08 | 0x20,0644,0);
         var w = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
         w.init(os, "UTF-8", 0, 0);
         var serializer = new XMLSerializer();
         w.writeString(serializer.serializeToString(document));
         w.close();
      } else {
         throw "URL scheme not supported: "+urlSpec;
      }
   },
   doesFileURLExist: function(furl) {
      var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
      var url = ioService.newURI(furl,null,null);
      url = url.QueryInterface(Components.interfaces.nsIFileURL);
      return url.file.exists();
   },
   showOptions: function() {
      //alert(this.atomicService.config.configURL);
      var data = {
         configURL: this.atomicService.config.configURL,
         autoTitle: this.atomicService.config.autoTitle,
         defaultType: this.atomicService.config.defaultType
      }
      window.openDialog("chrome://atomic/content/options.xul","options","chrome,modal",data);
      if (data.ok) {
         this.atomicService.config.configURL = data.configURL;
         this.atomicService.config.autoTitle = data.autoTitle;
         this.atomicService.config.defaultType = data.defaultType;
         this.atomicService.config.updatePreferences();
      }      
   },
   addSelected: function() {
      try {
         var item = this.services.getSelected();
      } catch (ex) {
         alert(ex);
      }
      if (!item) {
         this.addService();
      } else if (item.type=="service") {
         this.addService();
      } else if (item.type=="collection" || item.type=="workspace") {
         this.services.newCollection();
      }
   },
    addService: function() {
      var data = {
         url: "http://",
         title: "My Service",
         author: null,
         username: null,
         password: null
      };
      window.openDialog("chrome://atomic/content/new-service.xul","add service","chrome,resizable,modal",data);
      if (data.save) {
         var servicesE = this.atomicService.config.document.documentElement.getElementsByTagNameNS(ATOMIC_CONFIG_NS,"services").item(0);
         var service = null;
         if (!data.fakeIntrospection) {
            var link = servicesE.ownerDocument.createElementNS(ATOMIC_CONFIG_NS,"link");
            link.setAttribute("title",data.title);
            link.setAttribute("href",data.url);
            link.setAttribute("author",data.author);
            link.setAttribute("username",data.username);
            link.setAttribute("password",data.password);
            if (data.isGoogle) {
               link.setAttribute("rel","google");
            }
            link.setAttribute("type","application/atomserv+xml");
            servicesE.appendChild(link);
            service = this.atomicService.getAtomService(data.url,link);
            service.title = data.title;
            service.author = data.author;
            service.username = data.username;
            service.password = data.password;
         } else {
            var serviceE = servicesE.ownerDocument.createElementNS(ATOM_PUB_INTROSPECT_NS,"service");
            var at = data.username.indexOf('@');
            var alias = at<0 ? data.username : data.username.substring(0,at);
            var url = "http://"+alias+".blogspot.com/";
            serviceE.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:base",url);
            serviceE.setAttribute("title",data.title);
            serviceE.setAttribute("author",data.author);
            serviceE.setAttribute("username",data.username);
            serviceE.setAttribute("password",data.password);
            if (data.isGoogle) {
               serviceE.setAttribute("rel","google");
            }
            servicesE.appendChild(serviceE);
            var workspace =serviceE.ownerDocument.createElementNS(ATOM_PUB_INTROSPECT_NS,"workspace");
            workspace.setAttribute("title",data.title);
            serviceE.appendChild(workspace);
            var collection = workspace.ownerDocument.createElementNS(ATOM_PUB_INTROSPECT_NS,"collection");
            collection.setAttribute("title","Blog");
            collection.setAttribute("href",data.url);
            workspace.appendChild(collection);
            service = this.atomicService.getAtomService(url,serviceE);
            service.title = data.title;
            service.author = data.author;
            service.username = data.username;
            service.password = data.password;
            service.loadIntrospection(serviceE);
            service.blogspot = true;
            this.atomicService.blogspotLogin(service.username,service.password,{
               timeout: _TIMEOUT_,
               onSuccess: function(auth) {
                  service.authorization = "GoogleLogin auth="+auth;
               },
               onFailure: function(status,doc,text) {
                  alert("("+status+") Failed to login as "+service.username+" for blog:\n"+service.url+"\n"+text);
               }
            });
         }
         this.storeDocument(this.atomicService.config.configURL,this.atomicService.config.document);
         this._initServiceDisplay(service);
      }
   },
   doCopy: function() {
      alert("copy");
      var item = this.services.getSelected();
      this.copied = item;
   },
   doPaste: function() {
      alert("paste");
      if (this.copied.type=="service") {
         alert("You cannot paste a service.");
      } else if (this.copied.type=="collection") {
      }
   },
   removeSelected: function() {
      var item = this.services.getSelected();
      if (!item) {
         return;
      }
      if (item.type=="service") {
         if (confirm("Are you sure you want to delete "+item.service.title+" via "+item.service.url)) {
            var servicesE = this.atomicService.config.document.documentElement.getElementsByTagNameNS(ATOMIC_CONFIG_NS,"services").item(0);
            servicesE.removeChild(item.service.source);
            this.storeDocument(this.atomicService.config.configURL,this.atomicService.config.document);
            delete this.atomicService.services[item.service.url];
            this.services.notifyServiceRemoval(item.service.url);
            this.setStatusMessage();
         }
      } else if (item.type=="collection") {
         if (confirm("Are you sure you want to delete the collection "+item.collection.url)) {
            var currentApp = this;
            this.setStatusMessage("Deleting collection...");
            item.workspace.removeByDelete(item.collection, {
               timeout: _TIMEOUT_,
               onSuccess: function() {
                  currentApp.setStatusMessage();
                  //currentApp.servicesRDF.removeResource(context.workspace.service.url,context.workspace.id,collection.url);
                  currentApp._updateServiceDisplay(item.service);
               },
               onFailure: function(status,message) {
                  alert("("+status+") "+message);
               }
            });
         }
      }
   },
   reloadSelected: function() {
      var item = this.services.getSelected();
      if (!item) {
         return;
      }
      if (item.type=="service") {
         this.reloadService(item.service);
      } else if (item.type=="collection") {
         var oldXML = item.collection.feed.xml;
         var oldEntries = item.collection.feed.entries;
         var currentApp = this;
         item.collection.feed.load({
            timeout: currentApp.timeout,
            onSuccess: function() {
               alert(item.collection.url+" reloaded.");
            },
            onFailure: function(status,msgDoc) {
               item.collection.feed.xml = oldXML;
               item.collection.feed.entries = oldEntries;
               LOG.severe(status+" "+msgDoc.substring(0,100));
               alert(item.collection.url+" reload failed!");
               //alert(currentApp.messages.format("failedToLoad",currentApp.collection.feed.url,status,msgDoc.substring(0,100)));
            }
         });
      }
   },
   reloadServiceByURL: function(url) {
      var service = this.atomicService.services[url];
      if (service) {
         this.reloadService(service);
      }
   },
   reloadService: function(service) {
      LOG.debug("Attempting to load service "+service.url);
      var currentApp = this;
      try {
          var doLoad = function(target,online) {
             target.introspect({
                timeout: _TIMEOUT_,
                onSuccess: function() {
                   LOG.debug(service.url+" is loaded!");
                   setTimeout(function() {
                      alert("Service "+target.title+" has been reloaded.");
                   },500);
                   currentApp._updateServiceDisplay(target);
                },
                onFailure: function(status,doc,text) {
                   if (status<0 || status!=0) {
                      if (status<0) {
                         alert("Failed handle service "+target.url+" due to:\n"+text);
                      } else {
                         alert("Failed to contact service "+target.url+" ("+status+") due to:\n"+(doc ? doc.documentElement.textContent.substring(0,200) : ""));
                      }
                   } else if (online) {
                      alert("Service "+target.title+" has gone offline.");
                   } else {
                      alert("Service "+target.title+" still offline.");
                   }
                }
             });
          }
          doLoad(service,service.loaded);
      } catch(ex) {
         alert("Failed to load "+service.title+": "+ex);
         if (!service.loaded) {
            alert("Service "+service.title+" still offline.");
         }
      }
      
   },
   showServiceByURL: function(url) {
      var service = this.atomicService.services[url];
      if (!service.loaded) {
         alert("This service is not online.  Please try reloading the service to see if it is now available.");
         return;
      }
      this.showService(service);
   },
   showService: function(service) {
      window.openDialog("chrome://atomic/content/service.xul",service.url,"chrome,resizable",{
         title: service.title,
         timeout: _TIMEOUT_,
         url: service.url,
         xsltCache: this.xsltCache,
         logLevel: LOG.level,
         plugins: this.atomicService.plugins,
      }
      );
   },
   showCollection: function(collection) {
      window.openDialog("chrome://atomic/content/collection.xul",collection.url,"chrome,resizable",{
         title: collection.title,
         timeout: _TIMEOUT_,
         collection: collection,
         xsltCache: this.xsltCache,
         logLevel: LOG.level,
         plugins: this.atomicService.plugins
      }
      );
   }
}

